/*
 * Copyright (c) 2023 Cshoo Org. All Rights Reserved.
 */

package org.cshoo.tattoo.authorize.domain.service;

import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.IteratorUtils;
import org.apache.commons.lang.StringUtils;
import org.cshoo.tattoo.authorize.domain.bo.FrontPermissionBo;
import org.cshoo.tattoo.authorize.domain.constant.FrontPermissionType;
import org.cshoo.tattoo.authorize.domain.po.App;
import org.cshoo.tattoo.authorize.domain.po.Button;
import org.cshoo.tattoo.authorize.domain.po.Menu;
import org.cshoo.tattoo.authorize.domain.po.Role;
import org.cshoo.tattoo.authorize.domain.repository.*;
import org.cshoo.tattoo.embed.infra.entity.User;
import org.cshoo.tattoo.embed.infra.utils.UserUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 杨翼
 * @since 1.0
 */
@Service
@Slf4j
public class AppService {

    @Resource
    private IUserRepository userRepository;

    @Resource
    private IAppRepository appRepository;

    @Resource
    private IRoleRepository roleRepository;

    @Resource
    private IMenuRepository menuRepository;

    @Resource
    private IButtonRepository buttonRepository;

    /**
     * 查询当前用户的前端权限信息
     * 包括菜单按钮
     *
     * @param appCode 编号
     * @return 权限信息
     */
    public List<FrontPermissionBo> queryCurrentUserFrontPermissions(String appCode) {
        List<FrontPermissionBo> result = Lists.newArrayList();
        App app = appRepository.findAppByCode(appCode);
        User user = UserUtils.getUser();
        if (Objects.isNull(user) || StringUtils.isBlank(user.getUsername()) || StringUtils.equals("admin", user.getUsername())) {
            return Lists.newArrayList();
        }
        String username = user.getUsername();
        Long userId = userRepository.findUserByUsername(username).getId();
        List<Role> roles = roleRepository.findRolesByUserId(userId);
        List<Long> roleIds = roles.stream().map(Role::getId).distinct().collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(roleIds)) {
            List<Menu> rootMenus = menuRepository.findRootMenus(app.getId());
            List<Menu> menus = menuRepository.findByAppIdAndRoleIds(app.getId(), roleIds);
            List<Long> menuIds = menus.stream().map(Menu::getId).collect(Collectors.toList());
            List<Button> buttons = CollectionUtils.isNotEmpty(menuIds) ?
                    buttonRepository.findByRoleIdsAndMenuIds(roleIds, menuIds) : Lists.newArrayList();

            Queue<FrontPermissionBo> queue = new ArrayDeque<>();
            for (Menu rootMenu : rootMenus) {
                FrontPermissionBo frontPermissionBo = new FrontPermissionBo();
                BeanUtils.copyProperties(rootMenu, frontPermissionBo);
                this.chooseEnterQueue(frontPermissionBo, queue, menus, buttons);
            }
            result.addAll(queue);

            while (!queue.isEmpty()) {
                FrontPermissionBo frontPermission = queue.poll();
                if (frontPermission.getType() == FrontPermissionType.FOLDER.getValue()) {
                    menus.stream().filter(mn -> Objects.equals(mn.getParentId(), frontPermission.getId())).forEach(mn -> {
                        FrontPermissionBo child = new FrontPermissionBo();
                        BeanUtils.copyProperties(mn, child);
                        this.chooseEnterQueue(child, queue, menus, buttons);
                        frontPermission.getChildren().add(child);
                    });
                } else if (frontPermission.getType() == FrontPermissionType.MENU.getValue()) {
                    buttons.stream().filter(bt -> Objects.equals(bt.getMenuId(), frontPermission.getId()))
                            .forEach(bt -> {
                                FrontPermissionBo child = new FrontPermissionBo();
                                BeanUtils.copyProperties(bt, child);
                                child.setType(FrontPermissionType.BUTTON.getValue());
                                frontPermission.getChildren().add(child);
                            });
                }
            }
        }

        return result;
    }

    /**
     * 选择进入队列
     * 由于查询根菜单时为按权限查，是把所有根菜单都查了出来
     * 因此对于菜单类型的需要判断权限再加入队列
     *
     * @param frontPermissionBo 前端权限bo
     * @param queue             队列
     * @param menus             菜单列表
     * @param buttons           按钮列表
     */
    private void chooseEnterQueue(FrontPermissionBo frontPermissionBo, Queue<FrontPermissionBo> queue, List<Menu> menus, List<Button> buttons) {
        boolean hasChild = false;
        boolean hasPermission = true;
        if (frontPermissionBo.getType() == FrontPermissionType.FOLDER.getValue()) {
            hasChild = menus.stream().anyMatch(mn -> Objects.equals(mn.getParentId(), frontPermissionBo.getId()));
        } else if (frontPermissionBo.getType() == FrontPermissionType.MENU.getValue()) {
            hasPermission = menus.stream().anyMatch(mn -> Objects.equals(mn.getId(), frontPermissionBo.getId()));
            hasChild = buttons.stream().anyMatch(bt -> Objects.equals(bt.getMenuId(), frontPermissionBo.getId()));
        }
        if (hasChild && hasPermission) {
            frontPermissionBo.setChildren(Lists.newArrayList());
            queue.offer(frontPermissionBo);
        }
    }

    /**
     * 判断此编号是否存在
     *
     * @param code app编号
     * @return 判断结果
     */
    public boolean checkAppCodeExists(String code) {
        App app = appRepository.findAppByCode(code);
        log.info("app:{}", app);
        return Objects.nonNull(app);
    }

    /**
     * 添加一个app
     *
     * @param app app
     */
    public void addApp(App app) {
        log.info("app:{}", app);
        appRepository.save(app);
    }

    /**
     * 修改app，这里修改app信息的name,uri,description字段
     *
     * @param app app
     */
    public void updateApp(App app) {
        log.info("app:{}", app);
        app.setLastModifiedBy(UserUtils.getUser().getUsername());
        app.setLastModifiedDate(new Date());
        appRepository.updateApp(app);
    }

    /**
     * 删除app
     *
     * @param id id
     */
    public void deleteApp(Long id) {
        log.info("id:{}", id);
        appRepository.deleteById(id);
    }

    /**
     * 查询app
     *
     * @return app列表
     */
    @SuppressWarnings("unchecked")
    public List<App> findAllApp() {
        Iterable<App> apps = appRepository.findAll();
        return IteratorUtils.toList(apps.iterator());
    }

}
